﻿#include "SharedMemoryImpl.h"

#if defined(_WIN32) || defined(WIN32) || defined(WIN64) || defined(_WIN64)
#include <Windows.h>

class SharedMemoryImpl_Win : public SharedMemoryImpl
{
public:
    explicit SharedMemoryImpl_Win(const std::string &key);
    ~SharedMemoryImpl_Win();
    bool Create(size_t size) override;
    bool Attach(int permissions) override;
    bool Detach() override;
    bool IsAttached() const override;

    int Write(unsigned char *data, size_t size) override;
    int Read(unsigned char *buf, size_t size) override;

protected:
    HANDLE Handle();
    bool CleanHandle();

private:
    HANDLE hand{nullptr};
};

SharedMemoryImpl_Win::SharedMemoryImpl_Win(const std::string &key)
    : SharedMemoryImpl(key)
{
}

SharedMemoryImpl_Win::~SharedMemoryImpl_Win()
{
    if (memory)
    {
        UnmapViewOfFile(memory);
        memory = nullptr;
        size = 0;
    }

    CleanHandle();
}

bool SharedMemoryImpl_Win::Create(size_t size)
{
    hand = CreateFileMappingA(INVALID_HANDLE_VALUE,
                              nullptr,
                              PAGE_READWRITE,
                              0,
                              size,
                              key.c_str());

    return hand != nullptr && Attach(FILE_MAP_READ | FILE_MAP_WRITE);
}

bool SharedMemoryImpl_Win::Attach(int permissions)
{
    if (IsAttached())
    {
        return true;
    }
    memory = MapViewOfFile(Handle(), permissions, 0, 0, 0);

    if (nullptr == memory)
    {
        CleanHandle();
        return false;
    }
    MEMORY_BASIC_INFORMATION info;
    if (!VirtualQuery(memory, &info, sizeof(info)))
    {
        CleanHandle();
        return false;
    }
    size = info.RegionSize;
    return true;
}

bool SharedMemoryImpl_Win::Detach()
{
    if (!memory)
    {
        return true;
    }

    // umap memory
    if (!UnmapViewOfFile(memory))
    {
        return false;
    }
    memory = nullptr;
    size = 0;

    // close handle
    return CleanHandle();
}

bool SharedMemoryImpl_Win::IsAttached() const
{
    return memory != nullptr && size > 0;
}

int SharedMemoryImpl_Win::Write(unsigned char *data, size_t size)
{
    auto buflen = min(this->size, size);
    Lock();
    CopyMemory(memory, data, buflen);
    Unlock();
    return buflen;
}

int SharedMemoryImpl_Win::Read(unsigned char *buf, size_t size)
{
    auto buflen = min(this->size, size);
    Lock();
    CopyMemory(buf, this->memory, buflen);
    Unlock();
    return buflen;
}

HANDLE SharedMemoryImpl_Win::Handle()
{
    if (!hand)
    {
        if (key.empty())
        {
            return nullptr;
        }

        hand = OpenFileMappingA(FILE_MAP_READ | FILE_MAP_WRITE, false, key.c_str());
        if (!hand)
        {
            return nullptr;
        }
    }
    return hand;
}

bool SharedMemoryImpl_Win::CleanHandle()
{
    if (hand != nullptr && !CloseHandle(hand))
    {
        hand = nullptr;
        return false;
    }
    hand = nullptr;
    return true;
}

std::shared_ptr<SharedMemoryImpl> SharedMemoryImpl::CreateImpl(const std::string &key)
{
    return std::make_shared<SharedMemoryImpl_Win>(key);
}

#endif // win32